<?php
/*
composant de controle d'acces pour KAP

version SQL

file version 2005-02-15 15:09:13
*/

class kap_accessmanager extends kap_core_component {
/*------------------------------------------------------------------------------------------------
propriétés
------------------------------------------------------------------------------------------------*/
	var $default_parameters=array(
		'useGroups'=> 1, // usage des groupes
		'loginMethod'=>'post', // http|post,
		'httpRealm'=>BASE_HREF,
		'page401'=> 'xhtml/401.xhtml',
		'keyName'=>'clef2',
		'useCookie'=>true,
		'cookiePath'=>BASE_PATH,
		'preventAttacks'=>true, // prevenir les attaques par force brute
		'attackTime'=>300, // duree scannee pour l'attaque
		'attackMaxAttempts'=>300 // tentatives de connexion maximales durant cette duree
	);
/*------------------------------------------------------------------------------------------------
                                                                                methodes publiques
------------------------------------------------------------------------------------------------*/
	/* contrôle l'acces de l'utilisateur courant sur l'uri requise
	produit une page d'echec si précisée, et quitte en cas d'echec. Sinon ,retourne true
	*/
	function checkUriUserPermission($uri=NULL) {
		if ($uri===NULL) $uri=CALL_URI;
		// kapLog("accessManager: checking rights for ".$uri);
		// uri publique ?
		if (kapUriPermissionIsPublic($uri)) {
			kapLog("accessManager: $uri is public, no access control");
			return true;
		}
		// identification
		if (!$userId=$this->authenticateUser()) {
			kapLog("accessManager: no user logged, access denied for $uri");
			//$this->destroyCookie();
			return false;
		}
		kapLog("accessManager: user $userId is logged in");
		// recherche des groupes ?
		if ($this->parameters['useGroups']) {
			// recherche des groupes
			if (!$groups=kapGroupGetUserGroupIds($userId)) {
				kapLog("accessManager: user $userId has no groups, access denied for $uri");
				return false;
			}
			//kapLog("accessManager: groups used, user belonging to groups ".join(', ',$groups));
			// recherche des droits des groupes sur l'uri
			if (kapUriPermissionCheckIds($groups,$uri)) {
				kapLog("accessManager: groups ".join(', ',$groups)." grant access to $uri");
				return true;
			} else {
				kapLog("accessManager: groups ".join(', ',$groups)."do not grant access to $uri, access denied");
				return false;
			}
		} else {
			// recherche des droits individuels sur l'uri
			if (kapUriPermissionCheckId($userId,$uri)) {
				kapLog("accessManager: access granted to $uri for user $userId");
				return true;
			} else {
				kapLog("accessManager: access denied to $uri for user $userId");
				return false;
			}
		}
		
		return true;
	}
	// invalide l'authentification et produit un message ou une page d'erreur
	function logOut($page401=NULL) {
		// invalide la connexion
		$key=$this->getKey();
		if (!$this->invalidateAuth($key)) return false;
		kapLog("user logged out, key was $key");
		$this->destroyCookie();
		return true;
		// production de la page d'erreur ?
		/*$this->failpage($page401);
		$this->C->ShutDown();*/
	}
	// produit la page d'echec d'authentification
	function failPage($page401=NULL) {
		if ($page401===NULL) $page401=$this->parameters['page401'];
		//if (is_file($page401)) $page401=kapFileContent($page401);
		kapPageSetTemplate($page401);
		kapPageSend();
		$this->C->ShutDown();
	}
/*------------------------------------------------------------------------------------------------
droits sur les uri
------------------------------------------------------------------------------------------------*/
	// dteermine si l'uri est publique (pas d'authentification) ou privée
	function isPublicUri($uri=CALL_URI) {
		return false;
	}
	// vérifie les droits d'une id utilisateur sur l'uri
	function checkIdOnUri($userId,$uri=CALL_URI) {
	
	}
	
	// vérifie les droits de plusieurs id (groupes) sur l'uri
	function checkIdsOnUri($ids=array(),$uri=CALL_URI) {
	
	}
/*------------------------------------------------------------------------------------------------
authentification et comptes
------------------------------------------------------------------------------------------------*/
	// tente d'identifier l'utilisateur courant, retourne son id
	function authenticateUser() {
		// un utilisateur est il déjà connecté ?
		if ($key=$this->getKey()) {
			if ($info=kapConnexionCheck($key)) {
				$this->defineUserConstants($info['user_id']);
				return $info['user_id'];
			} else {
				// clef invalide
				kapLog("accessManager: key $key is invalid");
				$this->destroyCookie();
				return false;
			}
		}
		kapLog("accessManager : no key found ($key), try login by ".$this->parameters['loginMethod']);
		// sinon, tentative de connexion
		switch ($this->parameters['loginMethod']) {
		case 'http':
			$userId=$this->httpLogin();
			break;
		case 'post':
			$userId=$this->postLogin();
			break;
		default: return false;
		}
		if (!$userId) {
			//kapLog("authentication failed");
			return false;
		}
		$this->defineUserConstants($userId);
		//echo "toto";
		// creation de la connexion
		if(!$key=$this->C->connexionOpen($userId)) return false;
		// stockage eventuel de la clef
		$this->storeKey($key);
		return $userId;
	}
	// tente de connecter un utilisateur par http, retourne l'id en cas de succès
	function httpLogin($realm=NULL) {
		//kapLog('tentative d\'authentification http');
		if ($realm==NULL) $realm=$this->parameters['httpRealm'];
		$realm=str_replace('"','\"',$realm);
		if (!isset($_SERVER['PHP_AUTH_USER'])) {
			header('WWW-Authenticate: Basic realm="'.$realm.'"');
			header('HTTP/1.0 401 Unauthorized');
			kapLog("accessManager: HTTP authentication failed, no user provided");
			return false;
			//exit;
		}
		if ($user_id=$this->tryLogin($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW'])) {
			kapLog("accessManager: user ".$_SERVER['PHP_AUTH_USER']." authenticated from HTTP");
			return $user_id;
		}
		// echec
		kapLog("accessManager: HTTP authentication failed for user ".$_SERVER['PHP_AUTH_USER']);
		return false;
	}
	// tente de connecter un utilisateur par les donnée post, retourne l'id en cas de succès
	function postLogin() {
		//kapLog('tentative d\'authentification post');
		if (!isset($_POST['kap_login']) or !isset($_POST['kap_pwd'])) return false;
		if ($user_id=$this->tryLogin($_POST['kap_login'],$_POST['kap_pwd'])) {
			kapLog("accessManager: user ".$_POST['kap_login']." authenticated from POST data");
			return $user_id;
		}
		kapLog("accessManager: POST authentication failed for user ".$_POST['kap_login']);
		return false;
	}
/*------------------------------------------------------------------------------------------------
                                                                                           groupes
------------------------------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------------------------
                                                                                      utilisateurs
------------------------------------------------------------------------------------------------*/
	//function getUserByLoginPwd
/*------------------------------------------------------------------------------------------------
                                                                                        connexions
------------------------------------------------------------------------------------------------*/
	function storeKey($key,$useCookie=NULL) {
		$info=$this->C->connexionGetInfo();
		//echo affvar($info);
		if ($useCookie===NULL) $useCookie=$this->parameters['useCookie'];
		if (!$useCookie) {
			// enregistrement en session
			//$_SESSION[$this->keyName]=$key;
			if (!kapStoreVar($this->parameters['keyName'],$key)) return false;
			$this->is_logged=true;
			return true;
		} else if ($useCookie) {
			$info=$this->C->connexionGetInfo();
			$timestamp=kapDateConvert('php_timestamp',$info['date_peremption']);
			$cookie_set=setcookie($this->parameters['keyName'], $key, $timestamp, $this->parameters['cookiePath']);
			kapLog("accessManager: setting cookie ".$this->parameters['keyName']."=$key, expiring ".date('Y-m-d H:i:s',$timestamp).", for path ".$this->parameters['cookiePath']);
			if (!$cookie_set) 
				trigger_error("couldn't set cookie ".$this->parameters['keyName']."=".$key."=>".$timestamp,E_USER_NOTICE);
			return $cookie_set;
		}
	}
	
	function getKey() {
		//echo affvar($_COOKIE[$this->parameters['keyName']]);
		$key=kapGetVarIn($this->parameters['keyName'],'cookie_post_get');
		//echo affvar($key);
		//echo affvar($_COOKIE);
		return $key;
	}
	
	function destroyCookie() {
		kapLog("accessManager: destroying cookie ".$this->parameters['keyName'].", for path ".$this->parameters['cookiePath']);
		return setCookie($this->parameters['keyName'],'',time()-1,$this->parameters['cookiePath']);
	}
/*------------------------------------------------------------------------------------------------
                                                                                  methodes privees
------------------------------------------------------------------------------------------------*/
	function accessDenied($failPage=NULL) {
		if ($failPage===NULL)
			$failPage=$this->parameters['failPage'];
		kapPageSetTemplate($failPage);
		kapPageSend();
		kapShutDown();
		exit;
	}

	function invalidateHttpAuth($redirection=NULL,$realm=NULL) {
		if ($realm==NULL) $realm=$this->parameters['httpRealm'];
		if ($redirection==NULL) $redirection=BASE_HREF;
		//header('location: '.$redirection);
		header('WWW-Authenticate: Basic realm="'.$realm.'"');
		header('HTTP/1.0 401 Unauthorized');
		//header('location: '.$redirection);
		/*$realm = mt_rand( 1, 1000000000 );
		header('WWW-Authenticate: Basic realm="'.$realm.'"');*/
		unset($_SERVER['PHP_AUTH_USER']);
		unset($_SERVER['PHP_AUTH_PW']);
		unset($_SERVER['HTTP_AUTHORIZATION']);
	}
	
	function invalidateAuth($key) {
		// invalidation de la connexion
		if (!kapConnexionClose($key)) return false;
		// invalidation de la clef
		kapDestroyVar($this->parameters['keyName']);
		if ($this->parameters['loginMethod']=='http') $this->invalidateHttpAuth();
		//$this->destroyCookie();
		return true;
	}

/*-------------------------------------------------------------------------------------------------
                                                                                   methodes privées
-------------------------------------------------------------------------------------------------*/
	function defineUserConstants($user_id) {
		define('USER_ID',$user_id);
		$info=kapUserGetById($user_id);
		//echo affvar($info);
		define('USER_SCREEN_NAME',$info['screen_name']);
		define('USER_STATUS',$info['status']);
	}
	
	// retourne un identifiant utilisateur si login et pwd sont valides
	// tente la detection d'attaques, ferme KAP le cas echeant
	// enregistre les echecs
	function tryLogin($login,$pwd) {
		// detection d'attaque
		if ($this->checkAttack()) {
			//kapAlert("Le site subit une attaque, tentatives de connexion par force brute");
			kapShutDown();
		}
		// identification utilisateur
		if ($user_id=$this->C->UserGetIdByLoginPwd($login,$pwd))
			return $user_id;
		// log de l'echec
		$this->logFailedLogin($login,$pwd);
		return false;
	}
	
	function logFailedLogin($login,$pwd) {
		$sql="INSERT INTO kap_log_echecs_connexion(ip,uri,login,pwd)"
		." VALUES('".IP."','".CALL_URI."','$login','$pwd')";
		return kapSqlExecute($sql);
	}
	
	// detecte une attaque par force brute (trop de tentatives de connexion dans un laps de temps donné)
	function checkAttack() {
		$time=date('YmdHis',time()-$this->parameters['attackTime']);
		$sql="SELECT COUNT(*) AS attempts FROM kap_log_echecs_connexion"
		." WHERE time>'$time'";
		if (!$result=kapSqlUniqueResult($sql)) return true; // erreur SQL, principe de precaution
		if (intval($result['attempts'])>=$this->parameters['attackMaxAttempts']) return true;
		// dans les limites autorisées
		return false;
	}
/*------------------------------------------------------------------------------------------------
	methode publique
------------------------------------------------------------------------------------------------*/

/*------------------------------------------------------------------------------------------------
	methode publique
------------------------------------------------------------------------------------------------*/
}
?>
